package com.aabte.auth.group.domain.model;

import com.aabte.auth.account.domain.model.Account;
import com.aabte.auth.account.domain.model.AccountId;
import com.aabte.auth.role.domain.model.Role;
import com.aabte.auth.role.domain.model.RoleId;
import com.aabte.auth.shared.model.BaseModelUseLongPK;
import com.aabte.commons.ddd.shared.DomainEntity;
import lombok.Getter;
import lombok.ToString;

import javax.persistence.*;
import java.util.Objects;
import java.util.Set;

import static javax.persistence.ConstraintMode.NO_CONSTRAINT;

@SuppressWarnings("all")
@Entity
@Table(name = "tb_group")
@Getter
@ToString(callSuper = true)
public class Group extends BaseModelUseLongPK implements DomainEntity<Group, GroupId> {

    @Embedded
    private GroupId groupId;

    @Embedded
    private GroupName groupName;

    @Enumerated(value = EnumType.ORDINAL)
    private Status status;

    @ManyToMany
    @JoinTable(name = "tb_account_group", joinColumns = {
            @JoinColumn(name = "group_pk", foreignKey = @ForeignKey(NO_CONSTRAINT))
    }, inverseJoinColumns = {
            @JoinColumn(name = "account_pk", foreignKey = @ForeignKey(NO_CONSTRAINT))
    })
    private Set<Account> accounts;

    @ManyToMany
    @JoinTable(name = "tb_group_role", joinColumns = {
            @JoinColumn(name = "group_pk", foreignKey = @ForeignKey(NO_CONSTRAINT))
    }, inverseJoinColumns = {
            @JoinColumn(name = "role_pk", foreignKey = @ForeignKey(NO_CONSTRAINT))
    })
    private Set<Role> roles;

    private Group() {
    }

    public Group(GroupId groupId, GroupName groupName) {
        this(groupId, groupName, Status.ENABLE);
    }

    public Group(GroupId groupId, GroupName groupName, Status status) {
        this.groupId = groupId;
        this.groupName = groupName;
        this.status = status;
    }

    @Override
    public boolean sameIdentityAs(Group group) {
        return this.groupId.equals(group.groupId);
    }

    @Override
    public GroupId identity() {
        return this.groupId;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Group group = (Group) o;
        return groupId.equals(group.groupId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(groupId);
    }

    public void disable() {
        this.status = Status.DISABLE;
    }

    public boolean containsAccount(AccountId accountId) {
        if (null == accountId) {
            throw new IllegalArgumentException("accountId is null.");
        }
        Set<Account> accounts = this.getAccounts();
        for (Account account : accounts) {
            if (Objects.equals(account.getAccountId(), accountId)) {
                return true;
            }
        }
        return false;
    }

    public void addAccount(Account account) {
        if (null == account) {
            throw new IllegalArgumentException("account is null.");
        }
        this.getAccounts().add(account);
    }

    public boolean containsRole(RoleId roleId) {
        if (null == roleId) {
            throw new IllegalArgumentException("roleId is null.");
        }
        Set<Role> roles = this.getRoles();
        for (Role role : roles) {
            if (Objects.equals(role.getRoleId(), roleId)) {
                return true;
            }
        }
        return false;
    }

    public void addRole(Role role) {
        if (null == role) {
            throw new IllegalArgumentException("role is null.");
        }
        this.getRoles().add(role);
    }
}
